package com.chenfan.process.biz.impl;

import com.alibaba.fastjson.JSONObject;
import com.chenfan.ccp.entity.BaseEntity;
import com.chenfan.ccp.executor.SmartExecutorService;
import com.chenfan.infra.constants.CommonBeanConstants;
import com.chenfan.infra.context.TenantContextHolder;
import com.chenfan.infra.context.UserContextHolder;
import com.chenfan.infra.vo.UserVO;
import com.chenfan.process.biz.ApprovalInternalBiz;
import com.chenfan.process.biz.ProcessBizService;
import com.chenfan.process.context.CounterSignContext;
import com.chenfan.process.dto.ApprovalDTO;
import com.chenfan.process.dto.ApprovalOperationDTO;
import com.chenfan.process.dto.CheckPermissionDTO;
import com.chenfan.process.dto.StartApprovalDTO;
import com.chenfan.process.entity.dto.InstanceSnapshotDTO;
import com.chenfan.process.entity.po.*;
import com.chenfan.process.enums.ProcessEnums;
import com.chenfan.process.service.*;
import com.chenfan.process.vo.ApprovalNodeVO;
import com.chenfan.process.vo.ApprovalOperationVO;
import com.chenfan.process.vo.StartApprovalVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author liuming
 * @date 2021/10/11
 */
@Lazy
@Slf4j
@Service
public class ApprovalInternalBizImpl implements ApprovalInternalBiz {

    private static final int THREAD_SIZE = 50;
    @Autowired
    private ApprovalService approvalService;
    @Autowired
    private ProcessDetailService processDetailService;
    @Autowired
    private ProcessService processService;
    @Autowired
    private ProcessBizService processBizService;
    @Autowired
    private ApprovalSnapshotPoService approvalSnapshotPoService;
    @Autowired
    private ProcessRelatedService processRelatedService;
    @Autowired
    private EventPublisherBiz eventPublisherBiz;

    @Override
    public List<ApprovalOperationVO> batchApproval(List<ApprovalOperationDTO> approvalList)
        throws InterruptedException, ExecutionException {
        log.info("获取到对应的审批参数:{}, 传递过来的租户id:{}, 用户信息:{}", JSONObject.toJSONString(approvalList), JSONObject.toJSONString(UserContextHolder.getUserVo()),  TenantContextHolder.getTenantId());
        if(CollectionUtils.isEmpty(approvalList)){
            return new ArrayList<>();
        }
        ExecutorService executorService = SmartExecutorService.newFixedThreadPool();
        ExecutorCompletionService<ApprovalOperationVO> executorCompletionService = new ExecutorCompletionService<>(executorService);
        List<ApprovalOperationDTO> approvals = approvalList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(e -> e.getApprovalId()))), ArrayList::new));
        for (ApprovalOperationDTO approval : approvals) {
            Long tenantId = Objects.nonNull(UserContextHolder.getUserVo()) ? UserContextHolder.getUserVo().getTenantId() : TenantContextHolder.getTenantId();
            executorCompletionService.submit(new Task(tenantId, approval));
        }
        List<ApprovalOperationVO> list = new ArrayList<>();
        for (ApprovalOperationDTO approval : approvals) {
            Future<ApprovalOperationVO> take = executorCompletionService.take();
            ApprovalOperationVO approvalOperationVO = take.get();
            list.add(approvalOperationVO);
        }
        return list;
    }

    @Override
    public List<ApprovalNodeVO> listApprovalNode(Collection<Long> approvalIdList) {
        // 根据审批id查询当前节点, 拿出最后的节点
        List<ApprovalPo> approvals = approvalService.listApprovalListWithoutOption(approvalIdList);
        if (CollectionUtils.isEmpty(approvals)) {
            return Collections.emptyList();
        }
        Map<Long, ProcessPo> processMap =
            processService.list().stream().collect(Collectors.toMap(BaseEntity::getId, e -> e));

//        Map<Long, ApprovalSnapshotPo> snapshotMap = approvalSnapshotPoService.listByApprovalId(approvalIdList).stream()
//            .collect(Collectors.toMap(ApprovalSnapshotPo::getApprovalId, e -> e, (e1, e2) -> e2));

        Map<Long, List<ProcessDetailPo>> detailMap = processDetailService.listProcessDetailByApprovalId(approvalIdList)
            .stream().collect(Collectors.groupingBy(ProcessDetailPo::getParentId));

        return approvals.stream().map(e -> {
            // 1. 找到当期审批的数据, detail 找到当前节点和下一个节点
            List<ProcessDetailPo> processDetailPos = detailMap.get(e.getApprovalId());
            ProcessDetailPo processDetailPo = processDetailPos.get(processDetailPos.size() - 1);
            ApprovalNodeVO node = ApprovalNodeVO.builder().approvalId(e.getApprovalId()).build();

            // 查找当前节点信息
            Long processId = processDetailPo.getProcessId();
            ProcessPo processPo = processMap.get(processId);

            if (!ProcessEnums.finish(e.getStatus())) {
                // 当前人员信息
                node.setUserId(e.getUserId());
                node.setUserName(e.getUserName());

                if(Objects.nonNull(processPo)){
                    node.setNodeTag(processPo.getProcessTag());
                    node.setNodeName(processPo.getProcessTitle());
                }
            }
            // 当前节点
            ProcessPo currentNode;

            List<ProcessRelatedPo> relatedPoList = processRelatedService.listProcessRelatedAfterProcessId(
                e.getApprovalTemplateId(), e.getVersion(), processDetailPo.getNextProcessId());
            boolean lastFlag = false;
            if (!relatedPoList.isEmpty()) {
                ProcessPo p = processMap.getOrDefault(relatedPoList.get(0).getProcessAfterId(), new ProcessPo());
                lastFlag = p.isLast();
            }

            if (ProcessEnums.finish(e.getStatus()) || lastFlag) {
                currentNode = processMap.getOrDefault(processDetailPo.getNextProcessId(), new ProcessPo());
            } else {
                currentNode = processMap.getOrDefault(processDetailPo.getProcessId(), new ProcessPo());
            }
            node.setNodeName(currentNode.getProcessTitle());
            node.setNodeTag(currentNode.getProcessTag());

            if (processDetailPo.isSpecialNode()) {
                // 如果下一个节点为特殊节点, 直接取出来
                Long groupId = processDetailPo.getGroupId();
                // 根据groupId查询对应的值
                List<ProcessRelatedPo> processRelatedPos = processRelatedService.listWithGroup(e.getApprovalTemplateId(), e.getVersion(), groupId);
                // 拿到下个节点id信息
                List<Long> afterIdList = processRelatedPos.stream()
                        .filter(el -> Objects.equals(el.getProcessBeforeId(), processDetailPo.getProcessId())).distinct()
                        .map(ProcessRelatedPo::getProcessAfterId).collect(Collectors.toList());
                // 下个节点
                String nextNodeName = afterIdList.stream().map(el -> {
                    ProcessPo p = processMap.getOrDefault(el, new ProcessPo());
                    return !StringUtils.isEmpty(p.getProcessTitle()) ? p.getProcessTitle() : "";
                }).collect(Collectors.joining(","));
                if (!ProcessEnums.finish(e.getStatus())) {
                    node.setNextNodeName(nextNodeName);
                }
            } else {
                ProcessPo nextNode = processMap.getOrDefault(processDetailPo.getNextProcessId(), new ProcessPo());
                if (!ProcessEnums.finish(e.getStatus())) {
                    node.setNextNodeName(nextNode.getProcessTitle());
                }
            }
            return node;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ApprovalNodeVO> listCurrentApprovalNode(Collection<Long> approvalIdList) {
        // 根据审批id查询当前节点, 拿出最后的节点
        List<ApprovalPo> approvals = approvalService.listApprovalListWithoutOption(approvalIdList);
        if (CollectionUtils.isEmpty(approvals)) {
            return Collections.emptyList();
        }
        Map<Long, ProcessPo> processMap = processService.list().stream().collect(Collectors.toMap(BaseEntity::getId, e -> e));

        Map<Long, List<ProcessDetailPo>> detailMap = processDetailService.listProcessDetailByApprovalId(approvalIdList).stream().collect(Collectors.groupingBy(ProcessDetailPo::getParentId));

        Map<Long, ApprovalSnapshotPo> snapshotMap = approvalSnapshotPoService.listByApprovalId(approvalIdList).stream().collect(Collectors.toMap(ApprovalSnapshotPo::getApprovalId, e -> e, (e1, e2) -> e2));

        return approvals.stream().map(e -> {
            // 1. 找到当期审批的数据, detail 找到当前节点和下一个节点
            List<ProcessDetailPo> processDetailPos = detailMap.get(e.getApprovalId());
            ProcessDetailPo processDetailPo = processDetailPos.get(processDetailPos.size() - 1);
            ApprovalNodeVO node = ApprovalNodeVO.builder().approvalId(e.getApprovalId()).build();

            // 根据groupId查询对应的值
            Long groupId = processDetailPo.getGroupId();

            InstanceSnapshotDTO instanceSnapshotDTO = JSONObject.parseObject(snapshotMap.get(e.getApprovalId()).getRelation(), InstanceSnapshotDTO.class);
            // 查找当前节点信息
            List<ProcessRelatedPo> processRelatedPo = instanceSnapshotDTO.getProcessRelatedPoList();
            // 拿到当前节点的下一个数据
            // 拿到下个节点id信息
            List<Long> afterIdList = processRelatedPo.stream()
                .filter(el -> Objects.equals(el.getProcessBeforeId(), processDetailPo.getProcessId())).distinct()
                .map(ProcessRelatedPo::getProcessAfterId).collect(Collectors.toList());
            if (Objects.nonNull(groupId)) {
                afterIdList = processRelatedPo.stream()
                        .filter(el -> Objects.equals(el.getGroupId(), processDetailPo.getGroupId())).distinct()
                        .map(ProcessRelatedPo::getProcessAfterId).collect(Collectors.toList());
            }

            // 如果有多个的话, 说明当前是有分支的情况走过来的. 直接取下一个即可?
            if (afterIdList.size() > 1 & !processDetailPo.isSpecialNode()) {
                afterIdList = Collections.singletonList(processDetailPo.getNextProcessId());
            }

            List<String> users = Arrays.asList(e.getUserId().split(","));
            String nodeName = afterIdList.stream().filter(x -> {
                ProcessPo p = processMap.getOrDefault(x, new ProcessPo());
                node.setNodeTag(p.getProcessTag());
                if (p.isDynamic()) {
                    return true;
                }
                return users.contains(p.getAssociateGroupId());
            }).map(el -> {
                ProcessPo p = processMap.getOrDefault(el, new ProcessPo());
                return !StringUtils.isEmpty(p.getProcessTitle()) ? p.getProcessTitle() : "";
            }).distinct().collect(Collectors.joining(","));
            node.setUserId(e.getUserId());
            node.setUserName(e.getUserName());
            node.setNodeName(nodeName);
            return node;
        }).collect(Collectors.toList());
    }

    @Override
    public ApprovalNodeVO getApprovalNode(Long approvalId) {
        List<ApprovalNodeVO> list = listApprovalNode(Collections.singletonList(approvalId));
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    class Task implements Callable<ApprovalOperationVO> {

        private final ApprovalOperationDTO approvalOperation;
        private final Long tenantId;

        public Task(Long tenantId, ApprovalOperationDTO approvalOperation) {
            this.approvalOperation = approvalOperation;
            this.tenantId = tenantId;
        }

        @Override
        public ApprovalOperationVO call() {
            Long approvalId = this.approvalOperation.getApprovalId();
            ApprovalOperationVO operation = ApprovalOperationVO.builder().approvalId(approvalId).success(Boolean.TRUE).build();
            MDC.put(CommonBeanConstants.TENANT_ID, tenantId);
            // 拿到审批ID
            try {
                ApprovalDTO.Params params =
                    ApprovalDTO.Params.builder().approvalFlag(approvalOperation.isApprovalFlag())
                        .parameter(approvalOperation.getParameter()).remark(approvalOperation.getRemark()).build();
                ApprovalDTO approval = ApprovalDTO.builder()
                        .nextUserId(approvalOperation.getNextUserId())
                        .nextUserName(approvalOperation.getNextUserName())
                        .param(params).build();
                UserVO user = new UserVO();
                user.setTenantId(tenantId);
                user.setUserId(approvalOperation.getUserId());
                user.setRealName(approvalOperation.getUserName());
                UserContextHolder.setUserVo(user);
                ApprovalPo approvalPo = processBizService.calculateNextOne(approvalId, approvalOperation.getUserId(), approval);
                // trigger event.
                eventPublisherBiz.triggerEvent();
                eventPublisherBiz.triggerNotify(approvalPo);
            } catch (Exception e) {
                log.warn("审批实例Id:{}批量审批告警,错误原因:{}", approvalId, e.getMessage());
                log.error("异常信息:",e);
                operation.setMessage(e.getMessage());
                operation.setSuccess(Boolean.FALSE);
            } finally {
                CounterSignContext.clear();
            }
            MDC.clear();
            return operation;
        }
    }

    @Override
    public Boolean checkPermission(CheckPermissionDTO checkPermission) {
        Collection<Long> approvalIdList = checkPermission.getApprovalIdList();
        Map<Long, ApprovalPo> approvalMap = approvalService.findApprovalList(approvalIdList).stream()
            .collect(Collectors.toMap(ApprovalPo::getApprovalId, e -> e, (e1, e2) -> e2));

        return approvalIdList.stream().allMatch(e -> {
            ApprovalPo approvalPo = approvalMap.get(e);
            // 传过来一个不存在的, 或者是审批已经结束
            if (Objects.isNull(approvalPo) || ProcessEnums.finish(approvalPo.getStatus())) {
                return false;
            }
            String[] groupIdArr = approvalPo.getUserId().split(",");
            boolean permissionCheck = false;
            for (String groupStr : groupIdArr) {
                if (checkPermission.getUserId().equals(Long.valueOf(groupStr.trim()))) {
                    permissionCheck = Boolean.TRUE;
                    break;
                }
            }
            return permissionCheck;
        });
    }

    @Override
    public List<StartApprovalVO> startBatchApproval(String projectName, String approvalTemplateCode,
        List<StartApprovalDTO> approvalList) throws ExecutionException, InterruptedException {
        ExecutorService executorService = SmartExecutorService.newFixedThreadPool();
        ExecutorCompletionService<StartApprovalVO> executorCompletionService =
            new ExecutorCompletionService<>(executorService);
        for (StartApprovalDTO approval : approvalList) {
            executorCompletionService.submit(new StartApprovalTask(projectName, approvalTemplateCode, approval));
        }
        List<StartApprovalVO> list = new ArrayList<>();
        for (StartApprovalDTO approval : approvalList) {
            Future<StartApprovalVO> take = executorCompletionService.take();
            StartApprovalVO startApprovalVO = take.get();
            list.add(startApprovalVO);
        }
        return list;
    }

    class StartApprovalTask implements Callable<StartApprovalVO> {

        private final StartApprovalDTO startApproval;
        private final String projectName;
        private final String approvalTemplateCode;

        public StartApprovalTask(String projectName, String approvalTemplateCode, StartApprovalDTO startApprovalDTO) {
            this.projectName = projectName;
            this.startApproval = startApprovalDTO;
            this.approvalTemplateCode = approvalTemplateCode;
        }

        @Override
        public StartApprovalVO call() throws Exception {
            StartApprovalVO startApprovalVO = new StartApprovalVO();
            startApprovalVO.setBusinessId(startApproval.getBusinessId());

            ApprovalDTO approvalDTO = ApprovalDTO.builder().projectName(projectName).useId(startApproval.getUserId())
                .userName(startApproval.getUserName()).nextUserName(startApproval.getNextUserName())
                .nextUserId(startApproval.getNextUserId()).nextUserName(startApproval.getNextUserName())
                .param(ApprovalDTO.Params.builder().parameter(startApproval.getParam()).build())
                .businessId(startApproval.getBusinessId())
                .businessCode(startApproval.getBusinessCode())
                .businessParam(startApproval.getBusinessParam())
                .checkField(ApprovalDTO.CheckField.builder().checkFields(startApproval.getCheckField()).build())
                .build();
            try {
                com.chenfan.process.entity.vo.StartApprovalVO start = processBizService.startApproval(projectName, approvalTemplateCode, approvalDTO);
                ApprovalPo approvalPo = start.getApprovalPo();
                startApprovalVO.setApprovalId(approvalPo.getApprovalId());
                startApprovalVO.setSuccess(true);
                eventPublisherBiz.triggerEvent();
                eventPublisherBiz.triggerNotify(approvalPo);
            } catch (Exception e) {
                startApprovalVO.setSuccess(false);
                startApprovalVO.setMessage(e.getMessage());
            } finally {
                CounterSignContext.clear();
            }
            return startApprovalVO;
        }
    }
}
